package com.atguigu.gmall.activity.service.impl;

import com.atguigu.gmall.activity.mapper.CouponInfoMapper;
import com.atguigu.gmall.activity.mapper.CouponRangeMapper;
import com.atguigu.gmall.activity.mapper.CouponUseMapper;
import com.atguigu.gmall.activity.service.CouponInfoService;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.model.activity.*;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.enums.CouponRangeType;
import com.atguigu.gmall.model.enums.CouponStatus;
import com.atguigu.gmall.model.enums.CouponType;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.product.BaseCategory3;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.model.product.SpuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ：Shine
 * @description：
 * @date ：2021/3/12 13:46
 */
@Service
public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponInfo> implements CouponInfoService {
    @Autowired
    private CouponInfoMapper couponInfoMapper;

    @Autowired
    private CouponRangeMapper couponRangeMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private CouponUseMapper couponUseMapper;

    @Override
    public IPage<CouponInfo> getCouponInfoPage(Page<CouponInfo> pageParam) {
        QueryWrapper<CouponInfo> couponInfoQueryWrapper = new QueryWrapper<>();
        couponInfoQueryWrapper.orderByDesc("id");
        IPage<CouponInfo> couponInfoIPage = couponInfoMapper.selectPage(pageParam, couponInfoQueryWrapper);

        //循环遍历向page对象中添加类型
        couponInfoIPage.getRecords().stream().forEach(couponInfo -> {
            //    赋值优惠券类型
            couponInfo.setCouponTypeString(CouponType.getNameByType(couponInfo.getCouponType()));
            //    赋值优惠券范围
            if (couponInfo.getRangeType() != null) {
                couponInfo.setRangeTypeString(CouponRangeType.getNameByType(couponInfo.getRangeType()));
            }
        });
        return couponInfoIPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveCouponRule(CouponRuleVo couponRuleVo) {
        //先删除couponRange ，更新couponInfo ，再新增couponRange ！
        QueryWrapper<CouponRange> couponRangeQueryWrapper = new QueryWrapper<>();
        couponRangeQueryWrapper.eq("coupon_id", couponRuleVo.getCouponId());
        couponRangeMapper.delete(couponRangeQueryWrapper);

        CouponInfo couponInfo = this.getById(couponRuleVo.getCouponId());
        couponInfo.setBenefitAmount(couponRuleVo.getBenefitAmount());
        couponInfo.setBenefitDiscount(couponRuleVo.getBenefitDiscount());
        couponInfo.setConditionAmount(couponRuleVo.getConditionAmount());
        couponInfo.setConditionNum(couponRuleVo.getConditionNum());
        couponInfo.setRangeType(couponRuleVo.getRangeType().name());
        couponInfo.setRangeDesc(couponRuleVo.getRangeDesc());
        this.updateById(couponInfo);

        List<CouponRange> couponRangeList = couponRuleVo.getCouponRangeList();
        for (CouponRange couponRange : couponRangeList) {
            couponRange.setCouponId(couponRuleVo.getCouponId());
            couponRangeMapper.insert(couponRange);
        }

    }

    @Override
    public Map<String, Object> findCouponRuleList(Long id) {
        HashMap<String, Object> map = new HashMap<>();
        QueryWrapper<CouponRange> couponRangeQueryWrapper = new QueryWrapper<>();
        couponRangeQueryWrapper.eq("coupon_id", id);
        List<CouponRange> couponRangeList = couponRangeMapper.selectList(couponRangeQueryWrapper);
        List<Long> rangeIdList = couponRangeList.stream().map(CouponRange::getRangeId).collect(Collectors.toList());

        CouponInfo couponInfo = this.getById(id);
        if (!CollectionUtils.isEmpty(rangeIdList)) {
            if (CouponRangeType.SPU.name().equals(couponInfo.getRangeType())) {
                List<SpuInfo> spuInfoList = productFeignClient.findSpuInfoBySpuIdList(rangeIdList);
                map.put("spuInfoList", spuInfoList);
            } else if (CouponRangeType.TRADEMARK.name().equals(couponInfo.getRangeType())) {
                List<BaseTrademark> trademarkList = productFeignClient.findBaseTrademarkByTrademarkIdList(rangeIdList);
                map.put("trademarkList", trademarkList);
            } else {
                List<BaseCategory3> category3List = productFeignClient.findBaseCategory3ByCategory3IdList(rangeIdList);
                map.put("category3List", category3List);
            }
        }
        return map;
    }

    @Override
    public List<CouponInfo> findCouponByKeyword(String keyword) {
        QueryWrapper<CouponInfo> couponInfoQueryWrapper = new QueryWrapper<>();
        couponInfoQueryWrapper.like("coupon_name", keyword);
        return couponInfoMapper.selectList(couponInfoQueryWrapper);
    }

    @Override
    public List<CouponInfo> findCouponInfo(Long skuId, Long activityId, Long userId) {
        //获取skuInfo信息
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        if (skuInfo == null) {
            return null;
        }
        //少时诵诗书
        //获取普通优惠券列表
        List<CouponInfo> couponInfoList = couponInfoMapper.selectCouponInfoList(skuInfo.getSpuId(), skuInfo.getCategory3Id(), skuInfo.getTmId(), userId);

        //获取活动优惠券列表
        if (activityId != null) {
            List<CouponInfo> activityCouponInfoList = couponInfoMapper.selectActivityCouponInfoList(skuInfo.getSpuId(), skuInfo.getCategory3Id(), skuInfo.getTmId(), activityId, userId);

            //将普通优惠券和或活动优惠券进行和并
            couponInfoList.addAll(activityCouponInfoList);
        }

        return couponInfoList;
    }

    @Override
    @Transactional
    public void getCouponInfo(Long couponId, Long userId) {
        //    判断优惠券是否已经领取完毕
        CouponInfo couponInfo = this.getById(couponId);
        if (couponInfo.getTakenCount() >= couponInfo.getLimitNum()) {
            throw new GmallException(ResultCodeEnum.COUPON_LIMIT_GET);
        }

        //    一个用户只能领取一张优惠券
        QueryWrapper<CouponUse> couponUseQueryWrapper = new QueryWrapper<>();
        couponUseQueryWrapper.eq("coupon_id", couponId).eq("user_id", userId);
        Integer count = couponUseMapper.selectCount(couponUseQueryWrapper);
        if (count > 0) {
            throw new GmallException(ResultCodeEnum.COUPON_LIMIT_GET);
        }

        int newTakenCount = couponInfo.getTakenCount().intValue() + 1;
        couponInfo.setTakenCount(newTakenCount);
        this.updateById(couponInfo);

        //    当优惠券被领用后，更新couponUse表数据
        CouponUse couponUse = new CouponUse();
        couponUse.setCouponId(couponId);
        couponUse.setUserId(userId);
        couponUse.setCouponStatus(CouponStatus.NOT_USED.name());
        couponUse.setGetTime(new Date());
        couponUse.setExpireTime(couponInfo.getExpireTime());
        couponUseMapper.insert(couponUse);

    }

    @Override
    public IPage<CouponInfo> selectPageByUserId(Page<CouponInfo> pageParam, Long userId) {
        IPage<CouponInfo> page = couponInfoMapper.selectPageByUserId(pageParam, userId);
        return page;
    }

    @Override
    public Map<Long, List<CouponInfo>> findCartCouponInfo(List<CartInfo> cartInfoList, Map<Long, Long> skuIdToActivityIdMap, Long userId) {
        //初始化数据，后续使用
        Map<Long, SkuInfo> skuIdToSkuInfoMap = new HashMap<>();
        //优惠券范围规则数据
        Map<String, List<Long>> rangeToSkuIdMap = new HashMap<>();

        ArrayList<SkuInfo> skuInfoList = new ArrayList<>();

        for (CartInfo cartInfo : cartInfoList) {
            SkuInfo skuInfo = productFeignClient.getSkuInfo(cartInfo.getSkuId());
            skuInfoList.add(skuInfo);
            skuIdToSkuInfoMap.put(cartInfo.getSkuId(), skuInfo);
            //设置skuId对应的优惠券使用规则
            this.setRuleData(skuInfo, rangeToSkuIdMap);
        }


        //  查询所有优惠劵，但是没有与skuId 做对应关系
        if (CollectionUtils.isEmpty(skuInfoList)) return new HashMap<>();
        List<CouponInfo> allCouponInfoList = couponInfoMapper.selectCartCouponInfoList(skuInfoList, userId);

        for (CouponInfo couponInfo : allCouponInfoList) {
            //获取到对应的优惠券的range_id
            String rangeType = couponInfo.getRangeType();
            Long rangeId = couponInfo.getRangeId();

            //目的是为了获取skuId对应的优惠券列表，优惠券又分为活动优惠券和普通优惠券
            if (couponInfo.getActivityId() != null) {
                //记录同一个优惠券下的skuId
                ArrayList<Long> skuIdList = new ArrayList<>();

                //    活动优惠券
                Iterator<Map.Entry<Long, Long>> iterator = skuIdToActivityIdMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Long, Long> entry = iterator.next();
                    Long activityId = entry.getValue();
                    Long skuId = entry.getKey();

                    SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);

                    if (couponInfo.getActivityId().intValue() == activityId.intValue()) {
                        if (couponInfo.getRangeType().equals(CouponRangeType.SPU.name())) {
                            if (couponInfo.getRangeId().intValue() == skuInfo.getSpuId().intValue()) {
                                //    记录skuId属于哪个范围的优惠券
                                skuIdList.add(skuId);
                            }
                        } else if (couponInfo.getRangeType().equals(CouponRangeType.CATAGORY.name())) {
                            if (couponInfo.getRangeId().intValue() == skuInfo.getCategory3Id().intValue()) {
                                skuIdList.add(skuId);
                            }
                        } else if (couponInfo.getRangeType().equals(CouponRangeType.TRADEMARK.name())) {
                            if (couponInfo.getRangeId().intValue() == skuInfo.getTmId().intValue()) {
                                skuIdList.add(skuId);
                            }
                        }
                    }
                }

                couponInfo.setSkuIdList(skuIdList);
            } else {
                //    普通优惠券
                //    判断优惠券的使用范围
                if (rangeType.equals(CouponRangeType.SPU.name())) {
                    //  map中应该存储商品的skuId
                    couponInfo.setSkuIdList(rangeToSkuIdMap.get("range:1:" + rangeId));
                } else if (rangeType.equals(CouponRangeType.CATAGORY.name())) {
                    couponInfo.setSkuIdList(rangeToSkuIdMap.get("range:2:" + rangeId));
                } else {
                    couponInfo.setSkuIdList(rangeToSkuIdMap.get("range:3:" + rangeId));
                }
            }
        }

        //处理skuId对应的优惠券列表
        Map<Long, List<CouponInfo>> skuIdToCouponInfoListMap = new HashMap<>();
        //  因为这个优惠劵可以对用多个skuId 。
        for (CouponInfo couponInfo : allCouponInfoList) {
            //  获取活动，非活动的skuId列表。
            List<Long> skuIdList = couponInfo.getSkuIdList();
            if (!CollectionUtils.isEmpty(skuIdList)) {
                for (Long skuId : skuIdList) {
                    if (skuIdToCouponInfoListMap.containsKey(skuId)) {
                        List<CouponInfo> couponInfoList = skuIdToCouponInfoListMap.get(skuId);
                        couponInfoList.add(couponInfo);
                    } else {
                        List<CouponInfo> couponInfoList = new ArrayList<>();
                        couponInfoList.add(couponInfo);
                        skuIdToCouponInfoListMap.put(skuId, couponInfoList);
                    }
                }
            }
        }
        return skuIdToCouponInfoListMap;
    }

    @Override
    public List<CouponInfo> findTradeCouponInfo(List<OrderDetail> orderDetailList, Map<Long, ActivityRule> activityIdToActivityRuleMap, Long userId) {
        // 优惠券范围规则数据
        Map<String, List<Long>> rangeToSkuIdMap = new HashMap<>();
        // 初始化数据，后续使用
        Map<Long, OrderDetail> skuIdToOrderDetailMap = new HashMap<>();
        // 初始化数据，后续使用
        Map<Long, SkuInfo> skuIdToSkuInfoMap = new HashMap<>();
        // 所以购物项的sku列表
        List<SkuInfo> skuInfoList = new ArrayList<>();
        for(OrderDetail orderDetail : orderDetailList) {
            SkuInfo skuInfo = productFeignClient.getSkuInfo(orderDetail.getSkuId());
            skuInfoList.add(skuInfo);
            skuIdToOrderDetailMap.put(orderDetail.getSkuId(), orderDetail);
            skuIdToSkuInfoMap.put(orderDetail.getSkuId(), skuInfo);
            //设置规则数据
            //  this.setRuleData(skuInfo, orderDetail.getSkuId(), rangeToSkuIdMap);
            this.setRuleData(skuInfo,rangeToSkuIdMap);
        }

        /**
         * rangeType(范围类型)  1:商品(spuid) 2:品类(三级分类id) 3:品牌
         * rangeId(范围id)
         * 同一张优惠券不能包含多个范围类型，同一张优惠券可以对应同一范围类型的多个范围id（即：同一张优惠券可以包含多个spuId）
         * 示例数据：
         * couponId   rangeType   rangeId
         * 1             1             20
         * 1             1             30
         * 2             1             20
         */
        //  查询用户的优惠劵列表
        List<CouponInfo> allCouponInfoList = couponInfoMapper.selectTradeCouponInfoList(skuInfoList, userId);
        //  根据规则关联优惠券对应的skuId列表
        for(CouponInfo couponInfo : allCouponInfoList) {
            //  获取使用范围
            String rangeType = couponInfo.getRangeType();
            //  tmId,spuId,category3Id
            Long rangeId = couponInfo.getRangeId();

            // 如果是促销活动优惠券，那么该优惠券只能应用于该促销活动的sku
            if(null != couponInfo.getActivityId()) {
                Iterator<Map.Entry<Long, ActivityRule>> iterator = activityIdToActivityRuleMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Long, ActivityRule> entry = iterator.next();
                    Long activityId = entry.getKey();
                    ActivityRule activityRule = entry.getValue();
                    if(activityId.longValue() == couponInfo.getActivityId()) {
                        //活动对应的skuId
                        List<Long> activitySkuIdList = activityRule.getSkuIdList();
                        List<Long> skuIdList = new ArrayList<>();
                        //判断skuId是否在优惠券范围,如果在，这加入skuIdList
                        for(Long skuId : activitySkuIdList) {
                            SkuInfo skuInfo = skuIdToSkuInfoMap.get(skuId);
                            if(rangeType.equals(CouponRangeType.SPU.name())) {
                                if(skuInfo.getSpuId().longValue() == rangeId.longValue()) {
                                    skuIdList.add(skuId);
                                }
                            } else if (rangeType.equals(CouponRangeType.CATAGORY.name())) {
                                if(skuInfo.getCategory3Id().longValue() == rangeId.longValue()) {
                                    skuIdList.add(skuId);
                                }
                            } else {
                                if(skuInfo.getTmId().longValue() == rangeId.longValue()) {
                                    skuIdList.add(skuId);
                                }
                            }
                            couponInfo.setSkuIdList(skuIdList);
                        }
                    }
                }
            }else {
                if(rangeType.equals(CouponRangeType.SPU.name())) {
                    couponInfo.setSkuIdList(rangeToSkuIdMap.get("range:1:" + rangeId));
                } else if (rangeType.equals(CouponRangeType.CATAGORY.name())) {
                    couponInfo.setSkuIdList(rangeToSkuIdMap.get("range:2:" + rangeId));
                } else {
                    couponInfo.setSkuIdList(rangeToSkuIdMap.get("range:3:" + rangeId));
                }
            }
        }
        //  将相同优惠券对应的skuId列表合并
        //  同一张优惠劵可能对应着所有sku列表
        List<CouponInfo> resultCouponInfoList = new ArrayList<>();
        //  按照优惠劵的Id 将用户领取并未使用的优惠劵进行分组
        Map<Long, List<CouponInfo>> couponIdToListMap = allCouponInfoList.stream().collect(Collectors.groupingBy(couponInfo -> couponInfo.getId()));
        Iterator<Map.Entry<Long, List<CouponInfo>>> iterator = couponIdToListMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, List<CouponInfo>> entry = iterator.next();
            // Long couponId = entry.getKey();
            List<CouponInfo> couponInfoList = entry.getValue();

            // 优惠券对应的全部skuId
            List<Long> skuIdList = new ArrayList<>();
            for(CouponInfo couponInfo : couponInfoList) {
                skuIdList.addAll(couponInfo.getSkuIdList());
            }
            // 获取任意一张优惠券，设置对应的skuId列表
            CouponInfo couponInfo = couponInfoList.get(0);
            couponInfo.setSkuIdList(skuIdList);
            resultCouponInfoList.add(couponInfo);
        }

        //根据优惠券规则计算优惠金额
        //记录最优选项金额
        BigDecimal checkeAmount = new BigDecimal("0");
        //记录最优优惠券
        CouponInfo checkeCouponInfo = null;
        for(CouponInfo couponInfo : resultCouponInfoList) {
            //优惠券对应的全部skuId
            List<Long> skuIdList = couponInfo.getSkuIdList();
            //该优惠券对应的购物项总金额
            BigDecimal totalAmount = new BigDecimal("0");
            //该优惠券对应的购物项总个数
            int totalNum = 0;
            if(!CollectionUtils.isEmpty(skuIdList)) {
                for(Long skuId : skuIdList) {
                    OrderDetail orderDetail = skuIdToOrderDetailMap.get(skuId);
                    BigDecimal skuAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                    //  优惠劵对应 skuId 的总金额
                    totalAmount = totalAmount.add(skuAmount);
                    //  优惠劵对应 skuId 的个数
                    totalNum = totalNum + orderDetail.getSkuNum();
                }

            }

            /**
             * reduceAmount: 优惠后减少金额
             * isChecked:    是否最优选项（1：最优）
             * isSelect:     是否可选（1：满足优惠券使用条件，可选）（如：满减 100减10 200减30 500减70，当前可选：满减 100减10、200减30）
             */
            //优惠后减少金额
            BigDecimal reduceAmount = new BigDecimal("0");
            // 购物券类型 1 现金券 2 折扣券 3 满减券 4 满件打折券
            if(couponInfo.getCouponType().equals(CouponType.CASH.name())) {
                reduceAmount = couponInfo.getBenefitAmount();
                //标记可选
                couponInfo.setIsSelect(1);
            } else if (couponInfo.getCouponType().equals(CouponType.DISCOUNT.name())) {
                BigDecimal skuDiscountTotalAmount = totalAmount.multiply(couponInfo.getBenefitDiscount().divide(new BigDecimal("10")));
                reduceAmount = totalAmount.subtract(skuDiscountTotalAmount);
                //标记可选
                couponInfo.setIsSelect(1);
            }  else if (couponInfo.getCouponType().equals(CouponType.FULL_REDUCTION.name())) {
                if (totalAmount.compareTo(couponInfo.getConditionAmount()) > -1) {
                    reduceAmount = couponInfo.getBenefitAmount();
                    //标记可选
                    couponInfo.setIsSelect(1);
                }
            } else {
                if(totalNum >= couponInfo.getConditionNum().intValue()) {
                    BigDecimal skuDiscountTotalAmount1 = totalAmount.multiply(couponInfo.getBenefitDiscount().divide(new BigDecimal("10")));
                    reduceAmount = totalAmount.subtract(skuDiscountTotalAmount1);
                    //标记可选
                    couponInfo.setIsSelect(1);
                }
            }

            //  reduceAmount 计算最优的价格，checkeAmount 选中的最优金额
            if (reduceAmount.compareTo(checkeAmount) > 0) {
                checkeAmount = reduceAmount;
                checkeCouponInfo = couponInfo;
            }
            // 优惠后减少金额
            couponInfo.setReduceAmount(reduceAmount);
        }
        // 如果最优优惠券存在，则设置为默认选中
        if(null != checkeCouponInfo) {
            for(CouponInfo couponInfo : resultCouponInfoList) {
                if(couponInfo.getId().longValue() == checkeCouponInfo.getId().longValue()) {
                    couponInfo.setIsChecked(1);
                }
            }
        }
        return resultCouponInfoList;


    }

    @Override
    public void updateCouponInfoUseStatus(Long couponId, Long userId, Long orderId) {
        CouponUse couponUse = new CouponUse();
        couponUse.setOrderId(orderId);
        couponUse.setCouponStatus(CouponStatus.USED.name());
        couponUse.setUsingTime(new Date());

        QueryWrapper<CouponUse> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("coupon_id", couponId);
        queryWrapper.eq("user_id", userId);
        couponUseMapper.update(couponUse, queryWrapper);

    }

    //设置优惠券的存储规则，初始化操作
    private void setRuleData(SkuInfo skuInfo, Map<String, List<Long>> rangeToSkuIdMap) {
        //rangeToSkuIdMap  中的key就是key1，value是skuIdList
        String key1 = "range:1:" + skuInfo.getSpuId();
        if (rangeToSkuIdMap.containsKey(key1)) {
            List<Long> skuIdList = rangeToSkuIdMap.get(key1);
            skuIdList.add(skuInfo.getId());
        } else {
            List<Long> skuIdList = new ArrayList<>();
            skuIdList.add(skuInfo.getId());
            rangeToSkuIdMap.put(key1, skuIdList);
        }

        String key2 = "range:2:" + skuInfo.getCategory3Id();
        if (rangeToSkuIdMap.containsKey(key2)) {
            List<Long> skuIdList = rangeToSkuIdMap.get(key2);
            skuIdList.add(skuInfo.getId());
        } else {
            List<Long> skuIdList = new ArrayList<>();
            skuIdList.add(skuInfo.getId());
            rangeToSkuIdMap.put(key2, skuIdList);
        }

        String key3 = "range:3:" + skuInfo.getTmId();
        if (rangeToSkuIdMap.containsKey(key3)) {
            List<Long> skuIdList = rangeToSkuIdMap.get(key3);
            skuIdList.add(skuInfo.getId());
        } else {
            List<Long> skuIdList = new ArrayList<>();
            skuIdList.add(skuInfo.getId());
            rangeToSkuIdMap.put(key3, skuIdList);
        }
    }
}
